// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <iostream>
#include <cstring>
#include <time.h>
#include <chrono>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <algorithm>
#include <uuid/uuid.h>

#include "EpollModule.h"
#include "Log.h"

namespace master {

EpollModules::EpollModules()
{
    this->run = true;
    this->init = false;
}

int EpollModules::Init(uint32_t maxFdNum)
{
    if (this->init) {
        LOG_ERROR_FMT("Epoll already init");
        return ENC_FAIL;
    }
    this->init = true;
    this->maxFdNum = maxFdNum;

    this->epollState.epollFd = epoll_create(maxFdNum);
    if (this->epollState.epollFd < 0) {
        LOG_ERROR_FMT("Init epoll_create failed");
        return ENC_FAIL;
    }

    fdWEvent = new EpollFdEventT[maxFdNum];
    for (int i = 0; i < maxFdNum; i++) {
        fdWEvent->eventType = EV_IDLE;
        fdWEvent->writeData.fd = 0;
        fdWEvent->writeData.pClientWData = nullptr;
    }

    this->pQueue = std::make_shared<QueueModules>();
    if (this->pQueue == nullptr) {
        LOG_ERROR_FMT("Epoll init queue failed");
        return ENC_FAIL;
    }
    
    this->AddFd(CommonMaster::GetInstance().pSocketServer->socketGetFd(), EV_READ, false);
    this->CreateThreadInit();

    LOG_INFO_FMT("Epoll module init success");
    return ENC_OK;
}

int EpollModules::AddFd(int watchFd, int eventType, bool isETMode)
{
    if (watchFd > this->maxFdNum || watchFd < 0) {
        LOG_ERROR_FMT("Add watchFd %d failed", watchFd);
        return ENC_FAIL;
    }

    struct epoll_event event;
    event.events = 0;
    
    if (eventType & EV_READ) {
        event.events |= isETMode ? (EPOLLIN | EPOLLET) : EPOLLIN;
    }
    if (eventType & EV_WRITE) {
        event.events |= isETMode ? (EPOLLOUT | EPOLLET) : EPOLLOUT;
    }
    event.data.fd = watchFd;
    int ret = epoll_ctl(this->epollState.epollFd, EPOLL_CTL_ADD, watchFd, &event);
    if (ret != ENC_OK) {
        return ENC_FAIL;
    }
    return ENC_OK;
}

int EpollModules::ModifyFd(int watchFd, int eventType, WriteDataT &pWriteData)
{
    if (watchFd > this->maxFdNum || watchFd < 0) {
        LOG_ERROR_FMT("ModifyFd watchFd %d failed", watchFd);
        return ENC_FAIL;
    }

    struct epoll_event event;
    event.events = 0;
    
    if (eventType & EV_READ) {
        event.events |= EPOLLIN | EPOLLET;
    }
    if (eventType & EV_WRITE) {
        event.events |= EPOLLOUT | EPOLLET;
        this->fdWEvent[watchFd].eventType = EV_WRITE;
        this->fdWEvent[watchFd].writeData = pWriteData;
    }
    event.data.fd = watchFd;
    int ret = epoll_ctl(this->epollState.epollFd, EPOLL_CTL_MOD, watchFd, &event);
    if (ret != ENC_OK) {
        LOG_ERROR_FMT("Modify fd error=%d", errno);
        return ret;
    }
    
    return ENC_OK;
}

int EpollModules::DeleteFd(int watchFd, int eventType)
{
    if (watchFd > this->maxFdNum || watchFd < 0) {
        LOG_ERROR_FMT("DeleteFd too big watchFd %d error!", watchFd);
        return ENC_FAIL;
    }

    struct epoll_event event;
    event.events = 0;
    
    if (eventType & EV_READ) {
        event.events |= EPOLLIN | EPOLLET;
    }
    if (eventType & EV_WRITE) {
        event.events |= EPOLLOUT | EPOLLET;
    }
    event.data.fd = watchFd;
    int ret = epoll_ctl(this->epollState.epollFd, EPOLL_CTL_DEL, watchFd, &event);
    if (ret != ENC_OK) {
        return ENC_FAIL;
    }
    
    return ENC_OK;
}

int EpollModules::ReadData(void)
{
    while (this->run) {
        int fd = 0;
        uint32_t bufLen = 0;
        uint16_t cmdCtl = 0;
        std::string requestId;
        if (pQueue->Pop(QUEUE_READ, (uint8_t *)&fd, &bufLen, &cmdCtl, &fd, requestId) != 0) {
            CommonMaster::GetInstance().pSocketServer->socketClose(fd);
            LOG_ERROR_FMT("Read data queue pop failed");
            continue;
        }

        if (CommonMaster::GetInstance().commonMasterReadDataAnalysis(fd, requestId) != 0) {
            continue;
        }
    }

    return ENC_OK;
}

void *EpollModules::ReadDataThreadInit(void *pArgc)
{
    EpollModules *pThis = (EpollModules *) pArgc;
    pThis->ReadData();
    return pThis;
}

int EpollModules::DealData(void)
{
    auto redisClient = std::make_shared<RedisOperator>();
    while (this->run) {
        int fd = 0;
        uint32_t bufLen = 0;
        uint16_t cmdCtl = 0;
        std::string requestId;
        auto pData = std::shared_ptr<uint8_t>(new uint8_t[EPOLL_RECV_DATA_LEN], std::default_delete<uint8_t[]>());
        if (pQueue->Pop(QUEUE_DEAL, pData.get(), &bufLen, &cmdCtl, &fd, requestId) != 0) {
            LOG_ERROR_FMT("Deal data queue pop failed");
            CommonMaster::GetInstance().pSocketServer->socketClose(fd);
            continue;
        }

        if (cmdCtl == CLOUD_PHONE_DEVICE_REQ) {
            LOG_INFO_FMT("ReqId %s req begin assign", requestId.c_str());
        }

        if (CommonMaster::GetInstance().serverParseData(redisClient, cmdCtl, pData.get(), bufLen, fd, requestId) != 0) {
            LOG_ERROR_FMT("Server parse data failed");
            CommonMaster::GetInstance().pSocketServer->socketClose(fd); 
            continue;
        }
    }
    
    return ENC_OK;
}

void *EpollModules::DealDataThreadInit(void *pArgc)
{
    EpollModules *pThis = (EpollModules *) pArgc;
    pThis->DealData();
    return pThis;
}

int EpollModules::WriteData(void)
{
    // 写完之后是需要del write data;
    while (this->run) {
        int fd = 0;
        uint32_t bufLen = 0;
        uint16_t cmdCtl = 0;
        std::string requestId;
        auto pData = std::shared_ptr<uint8_t>(new uint8_t[EPOLL_RECV_DATA_LEN], std::default_delete<uint8_t[]>());
        if (pQueue->Pop(QUEUE_WRITE, pData.get(), &bufLen, &cmdCtl, &fd, requestId) != 0) {
            LOG_ERROR_FMT("Write data queue pop failed");
            CommonMaster::GetInstance().pSocketServer->socketClose(fd);
            continue;
        }

        if (CommonMaster::GetInstance().commonMasterWriteData(cmdCtl, pData.get(), bufLen, fd, requestId) != 0) {
            LOG_ERROR_FMT("Write data failed");
            CommonMaster::GetInstance().pSocketServer->socketClose(fd); 
            continue;
        }
    }
    return ENC_OK;
}

void *EpollModules::WriteDataThreadInit(void *pArgc)
{
    EpollModules *pThis = (EpollModules *) pArgc;
    pThis->WriteData();
    return pThis;
}

int EpollModules::CreateThreadInit(void)
{
    if (ENC_OK != pthread_create(&readId, NULL, ReadDataThreadInit, this)) {
        LOG_ERROR_FMT("Create read data thread failed errno = %d", errno);
        return ENC_FAIL;
    }

    if (ENC_OK != pthread_create(&dealId, NULL, DealDataThreadInit, this)) {
        LOG_ERROR_FMT("Create deal data thread failed errno = %d", errno);
        return ENC_FAIL;
    }

    if (ENC_OK != pthread_create(&writeId, NULL, WriteDataThreadInit, this)) {
        LOG_ERROR_FMT("Create write data thread failed errno = %d", errno);
        return ENC_FAIL;
    }
    LOG_INFO_FMT("Create init thread end");
    return ENC_OK;
}

std::string EpollModules::GenerateRequestId()
{
    char requestId[36];
    uuid_t uuid;
    uuid_generate(uuid);
    uuid_unparse(uuid, requestId);

    std:string requestIdStr;
    requestIdStr.assign(requestId);
    return requestIdStr;
}

int EpollModules::StoreSendData(uint8_t *inData, uint32_t inLen, int fd, uint16_t cmdCtl, std::string requestId)
{
    WriteDataT gWriteData;
    auto pData = std::shared_ptr<uint8_t>(new uint8_t[MAX_QUEUE_DATA_LEN], std::default_delete<uint8_t[]>());
    gWriteData.pClientWData = pData;

    gWriteData.fd = fd;
    gWriteData.cmdCtl = cmdCtl;
    gWriteData.requestId = requestId;
    gWriteData.bufLen = inLen;

    int ret = memcpy_s(gWriteData.pClientWData.get(), inLen, inData, inLen);
    if (ret != ENC_OK) {
        LOG_ERROR_FMT("Push write data mem copy failed ret = %d", ret);
        return ENC_FAIL;
    }
    ret = this->ModifyFd(fd, EV_WRITE, gWriteData); 
    if (ret != ENC_OK) {
        LOG_ERROR_FMT("Modify fd %d write error ret = %d", fd, ret);
        return ENC_FAIL;
    }
    return ENC_OK;
}

int EpollModules::Run(void)
{
    if (!this->run) {
        LOG_ERROR_FMT("Epoll run state is error");
        return ENC_FAIL;
    }

    while (this->run) {
        int eventNum = epoll_wait(this->epollState.epollFd, this->epollState.events, EPOLL_MAX_EVENT, TIME_OUT_INTERVAL);
        for (int i = 0; i < eventNum; i++) {
            if (this->epollState.events[i].data.fd == CommonMaster::GetInstance().pSocketServer->socketGetFd()) {
                int fd = 0;
                CommonMaster::GetInstance().pSocketServer->acceptNewConnect(&fd);

                this->AddFd(fd, EV_READ);
            } else if (this->epollState.events[i].events & EPOLLIN) {
                int epollInFd = this->epollState.events[i].data.fd;
                std::string requestId = GenerateRequestId();

                pQueue->Push(QUEUE_READ, (uint8_t *)&epollInFd, sizeof(int), 0, epollInFd, requestId);
            } else if (this->epollState.events[i].events & EPOLLOUT) {
                int epollOutFd = this->epollState.events[i].data.fd;

                pQueue->Push(QUEUE_WRITE, fdWEvent[epollOutFd].writeData.pClientWData.get(), fdWEvent[epollOutFd].writeData.bufLen, 
                    fdWEvent[epollOutFd].writeData.cmdCtl, fdWEvent[epollOutFd].writeData.fd, fdWEvent[epollOutFd].writeData.requestId);
                if (this->ModifyFd(fdWEvent[epollOutFd].writeData.fd, EV_READ, fdWEvent[epollOutFd].writeData) != 0) {
                    this->DeleteFd(fdWEvent[epollOutFd].writeData.fd, EV_WRITE | EV_READ);
                }
            }
        }
    }
    LOG_INFO_FMT("Epoll run state is stop");
    return ENC_OK;
}
};